﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Design.PluralizationServices;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Infrastructure.Interception;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Sharp.Domain.Model;
using Sharp.Domain.PlatformBoundedContext.Migrations;
using Sharp.Domain.PlatformBoundedContext.PlatformModule.Aggregates;
using Sharp.Domain.Repository.EF;
using Sharp.Infrastructure;
using Sharp.Infrastructure.Interfaces;

namespace Sharp.Domain.PlatformBoundedContext
{
    /// <inheritdoc />
    /// <summary>
    ///     平台数据访问上下文。
    /// </summary>
    //[DbConfigurationType(typeof(MyConfiguration))]
#if MYSQL
    [DbConfigurationType(typeof(MySql.Data.Entity.MySqlEFConfiguration))]
#endif
    public partial class Entities : SharpEntities
    {

        #region 构造函数

        static Entities()
        {

#if DEBUG
            Database.SetInitializer<Entities>(null);
#else
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<Entities, Configuration>());
#endif
        }

        /// <inheritdoc />
        /// <summary>
        ///     构造函数
        /// </summary>
        public Entities() : base("Entities")
        {
        }

        #endregion


        #region OnModelCreating

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);


            PluralizationService _service = PluralizationService.CreateService(new CultureInfo("en-US"));
            modelBuilder.Types().Configure(entity =>
                {
                    if (entity.ClrType.FullName != null &&
                        entity.ClrType.FullName.IndexOf("WorkFlow", StringComparison.CurrentCultureIgnoreCase) > -1)
                    {
                        return;
                    }


                    entity.ToTable("Platform_" + _service.Pluralize(entity.ClrType.Name));


                });

            modelBuilder.Configurations.AddFromAssembly(typeof(Entities).Assembly);
        }

        #endregion


        public override int SaveChanges()
        {
            List<AuditLog> AuditLogs = new List<AuditLog>();
            List<AuditBackupLog> AuditBackupLogs = new List<AuditBackupLog>();

            var changeTracker = ChangeTracker.Entries().Where(p => p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified);

            foreach (var entity in changeTracker)
            {
                if (entity.Entity.GetType().GetCustomAttributes(typeof(AllowAnonymousAttribute), false).Any())
                {
                    continue;
                }

                if (entity.Entity != null)
                {
                    var entityName = ObjectContext.GetObjectType(entity.Entity.GetType()).Name;
                    EntityState state = entity.State;
                    switch (entity.State)
                    {
                        case EntityState.Modified:
                            foreach (string prop in entity.OriginalValues.PropertyNames)
                            {
                                object currentValue = entity.CurrentValues[prop];
                                object originalValue = entity.GetDatabaseValues()[prop];//OriginalValues[prop];
                                if (!object.Equals(currentValue, originalValue) &&
                                    entity.Property(prop).IsModified
                                    && (prop != nameof(ILogModifiEntity.ModifiTime) || prop != nameof(ILogModifiEntity.ModifiedUserId)))
                                {
                                    AuditLogs.Add(new AuditLog
                                    {
                                        EntityName = entityName,
                                        RecordId = PrimaryKeyValue(entity),
                                        State = (int)state,
                                        StateName = state.ToString(),
                                        PropertyName = prop,
                                        OriginalValue = Convert.ToString(originalValue),
                                        NewValue = Convert.ToString(currentValue),
                                    });
                                }
                            }
                            break;
                        case EntityState.Added:
                            foreach (string prop in entity.CurrentValues.PropertyNames)
                            {
                                AuditLogs.Add(new AuditLog
                                {
                                    EntityName = entityName,
                                    RecordId = PrimaryKeyValue(entity),
                                    State = (int)state,
                                    StateName = state.ToString(),
                                    PropertyName = prop,
                                    OriginalValue = string.Empty,
                                    NewValue = Convert.ToString(entity.CurrentValues[prop]),
                                });

                            }

                            break;
                        case EntityState.Deleted:
                            AuditBackupLogs.Add(new AuditBackupLog()
                            {
                                EntityName = entityName,
                                RecordId = PrimaryKeyValue(entity),
                                Data = entity.Entity.ToJson()
                            });
                            break;
                        default:
                            break;
                    }
                }
            }

            var rowAffecteds = base.SaveChanges();

            if (AuditLogs.Any()|| AuditBackupLogs.Any())
            {
                this.Set<AuditBackupLog>().AddRange(AuditBackupLogs);
                this.Set<AuditLog>().AddRange(AuditLogs);

                base.SaveChanges();
            }
            return rowAffecteds;
        }

        private string PrimaryKeyValue(DbEntityEntry entry)
        {
            var objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity);
            if (null == objectStateEntry.EntityKey.EntityKeyValues)
            {
                return string.Empty;
            }
            return string.Join(",", objectStateEntry.EntityKey.EntityKeyValues.Select(item => item.Value.ToString()).ToArray());
        }
    }
}